package com.feeyo.groundservice.http.core

import android.util.Log
import com.feeyo.groundservice.base_util.FyLog
import com.feeyo.groundservice.base_util.serverTime
import com.feeyo.groundservice.http.constant.BUSINESS_CODE_EXCEPTION
import com.feeyo.groundservice.http.constant.DEFAULT_EXCEPTION_CODE
import com.feeyo.groundservice.http.constant.DEFAULT_NORMAL_CODE
import com.feeyo.groundservice.http.exception.BusinessException
import com.feeyo.groundservice.http.utils.toJson
import com.squareup.moshi.*
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type

/**
 * : JsonAdapter.Factory
 * 要把数据能适配到T上面来。比如 NewsChannelsBean
 *
 * Converts Java values to JSON, and JSON values to Java.
 *
 *  @GET("release/channel")
 *  suspend fun getNewsChannelsWithoutEnvelope(): HttpResult<NewsChannelsBean>
 */
class MoshiResultTypeAdapterFactory(private val baseBean: IBaseBean?) : JsonAdapter.Factory {

    /**
     * HttpResultWrapper
     *
     * 假设公司各个业务服务器返回的Http Json数据格式都差不多三个大字段（名称允许自定义不同）code[int] + msg[str] +data[T]
     * 这里我们统一约定称含有三个类似字段为包装数据wrapper data,每个Http请求正常都会含有这三个字段，不同的是data 中的数据，
     * 很自然的我们使用范型T 来表示。
     *
     * 根据项目自身的情况再次拓展一下让其更具有包容性
     *
     */
    override fun create(
        type: Type,
        annotations: MutableSet<out Annotation>,
        moshi: Moshi
    ): JsonAdapter<*>? {

        val rawType = type.rawType

        if (rawType != HttpResult::class.java) return null

        val dataType: Type =
            (type as? ParameterizedType)?.actualTypeArguments?.firstOrNull() ?: return null

        val dataTypeAdapter = moshi.nextAdapter<Any>(this, dataType, annotations)

        //Result<T> 范型解析出来
        return ResultTypeAdapter(dataTypeAdapter, baseBean)
    }


    /**
     * 返回请求需求的那个T
     *
     */
    class ResultTypeAdapter<T>(
        private val dataTypeAdapter: JsonAdapter<T>,
        private val baseBean: IBaseBean?
    ) : JsonAdapter<T>() {

        /**
         * Decodes a nullable instance of type T from the given reader.
         *
         */
        override fun fromJson(reader: JsonReader): T? {
            if (baseBean != null) {
                reader.beginObject()

                //一般都是code +msg + result/data
                var code: Int? = null
                var msg: String? = null
                var data: Any? = null
                var time: Long? = System.currentTimeMillis()//保存最新的时间戳

                while (reader.hasNext()) {
                    when (reader.nextName()) {
                        //根据不同服务器后台HTTP 报文字段 解析映射出code +msg + data
                        baseBean.getCodeKey() -> code = reader.nextString().toIntOrNull()
                        baseBean.getMsgKey() -> msg = reader.nextString()
                        baseBean.getDataKey() -> data = dataTypeAdapter.fromJson(reader)
                        baseBean.getServerTimeKey() -> time = reader.nextString().toLongOrNull()
                        else -> reader.skipValue()
                    }
                }

                reader.endObject()

                //打印结果
                val json = hashMapOf<String,Any?>().apply {
                    this[baseBean.getCodeKey()] = code
                    this[baseBean.getMsgKey()] = msg
                    this[baseBean.getServerTimeKey()] = time
                    this[baseBean.getDataKey()] = data
                }
                FyLog.json("result", json.toJson())

                //保存最新的服务器时间
                time?.let {
                    serverTime.value = it
                }

                // 这个字段要看看是否服务器是否是必传字段
                // 否则没有必要抛异常
                if (baseBean.hasCodeKey() && code == null)
                    throw JsonDataException("Expected field [code] not present.")

                // 这个字段要看看是否服务器是否是必传字段
                // 否则没有必要抛异常
                if (baseBean.hasMsgKey() && msg == null)
                    throw JsonDataException("Expected field [msg] not present.")

                if (baseBean.isRequestSuccess(code ?: DEFAULT_NORMAL_CODE)) {
                    return data as T
                } else {//业务返回的code，非正常业务状态，展示错误消息即可
                    if (msg.isNullOrEmpty())
                        msg = BUSINESS_CODE_EXCEPTION
                    throw BusinessException(
                        code ?: DEFAULT_EXCEPTION_CODE,
                        msg
                    )
                }

            } else {//baseBean == null 不是标准的code + msg +data 也没关系，只要自己构造BaseBean即可
                Log.e(javaClass.simpleName, "baseBean is null，请自己适配基础model")
                return dataTypeAdapter.fromJson(reader) as T
            }

        }


        /**
         * Encodes the given value with the given writer.
         * 后面再说吧
         *
         */
        override fun toJson(writer: JsonWriter, value: T?): Unit = TODO("Not yet implemented")


    }

}
